/* Copyright (c) 2007 Scott Lembcke
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
 
#include "chipmunk.h"

#include "ruby.h"
#include "rb_chipmunk.h"

ID id_static_shapes;
ID id_active_shapes;
ID id_bodies;
ID id_static_body;

VALUE c_cpSpace;

static VALUE
rb_cpSpaceAlloc(VALUE klass)
{
	cpSpace *space = cpSpaceAlloc();
	return Data_Wrap_Struct(klass, NULL, &cpSpaceFree, space);
}

static VALUE
rb_cpSpaceInitialize(VALUE self, VALUE iterations)
{
	cpSpace *space = SPACE(self);
	cpSpaceInit(space, NUM2INT(iterations));
	
	VALUE staticBody = Data_Wrap_Struct(c_cpBody, NULL, NULL, space->staticBody);
	rb_ivar_set(staticBody, id_parent, self);
	rb_ivar_set(self, id_static_body, staticBody);
	
	rb_ivar_set(self, id_static_shapes, rb_ary_new());
	rb_ivar_set(self, id_active_shapes, rb_ary_new());
	rb_ivar_set(self, id_bodies, rb_ary_new());
	
	return self;
}

static VALUE
rb_cpSpaceGetIterations(VALUE self)
{
	return INT2NUM(SPACE(self)->iterations);
}

static VALUE
rb_cpSpaceSetIterations(VALUE self, VALUE val)
{
	SPACE(self)->iterations = NUM2INT(val);
	return val;
}

static VALUE
rb_cpSpaceGetDamping(VALUE self)
{
	return rb_float_new(SPACE(self)->damping);
}

static VALUE
rb_cpSpaceSetDamping(VALUE self, VALUE val)
{
	SPACE(self)->damping = NUM2DBL(val);
	return val;
}

static VALUE
rb_cpSpaceGetGravity(VALUE self)
{
	return VNEW(SPACE(self)->gravity);
}

static VALUE
rb_cpSpaceSetGravity(VALUE self, VALUE val)
{
	SPACE(self)->gravity = *VGET(val);
	return val;
}

static VALUE
rb_cpSpaceGetStaticBody(VALUE self)
{
	return rb_ivar_get(self, id_static_body);
}

//TODO add material blocks

static VALUE
rb_cpSpaceAddShape(VALUE self, VALUE shape)
{
	cpSpaceAddShape(SPACE(self), SHAPE(shape));
	rb_ary_push(rb_ivar_get(self, id_active_shapes), shape);
	return shape;
}

static VALUE
rb_cpSpaceAddStaticShape(VALUE self, VALUE shape)
{
	cpSpaceAddStaticShape(SPACE(self), SHAPE(shape));
	rb_ary_push(rb_ivar_get(self, id_static_shapes), shape);
	return shape;
}

static VALUE
rb_cpSpaceAddBody(VALUE self, VALUE body)
{
	cpSpaceAddBody(SPACE(self), BODY(body));
	rb_ary_push(rb_ivar_get(self, id_bodies), body);
	return body;
}

static VALUE
rb_cpSpaceRemoveShape(VALUE self, VALUE shape)
{
	cpSpaceRemoveShape(SPACE(self), SHAPE(shape));
	return rb_ary_delete(rb_ivar_get(self, id_active_shapes), shape);
}

static VALUE
rb_cpSpaceRemoveStaticShape(VALUE self, VALUE shape)
{
	cpSpaceRemoveStaticShape(SPACE(self), SHAPE(shape));
	return rb_ary_delete(rb_ivar_get(self, id_static_shapes), shape);
}

static VALUE
rb_cpSpaceRemoveBody(VALUE self, VALUE body)
{
	cpSpaceRemoveBody(SPACE(self), BODY(body));
	return rb_ary_delete(rb_ivar_get(self, id_bodies), body);
}

static VALUE
rb_cpSpaceEachShape(VALUE self)
{
	rb_ary_each(rb_ivar_get(self, id_active_shapes));
	return self;
}

static VALUE
rb_cpSpaceEachStaticShape(VALUE self)
{
	rb_ary_each(rb_ivar_get(self, id_static_shapes));
	return self;
}

static VALUE
rb_cpSpaceEachBody(VALUE self)
{
	rb_ary_each(rb_ivar_get(self, id_bodies));
	return self;
}

static VALUE
rb_cpSpaceResizeStaticHash(VALUE self, VALUE dim, VALUE count)
{
	cpSpaceResizeStaticHash(SPACE(self), NUM2DBL(dim), NUM2INT(count));
	return Qnil;
}

static VALUE
rb_cpSpaceResizeActiveHash(VALUE self, VALUE dim, VALUE count)
{
	cpSpaceResizeActiveHash(SPACE(self), NUM2DBL(dim), NUM2INT(count));
	return Qnil;
}

static VALUE
rb_cpSpaceRehashStatic(VALUE self)
{
	cpSpaceRehashStatic(SPACE(self));
	return Qnil;
}

static VALUE
rb_cpSpaceStep(VALUE self, VALUE dt)
{
	cpSpaceStep(SPACE(self), NUM2DBL(dt));
	return Qnil;
}



void
Init_cpSpace(void)
{
	id_static_shapes = rb_intern("static_shapes");
	id_active_shapes = rb_intern("active_shapes");
	id_bodies = rb_intern("bodies");
	id_static_body = rb_intern("static_body");
	
	c_cpSpace = rb_define_class_under(m_Chipmunk, "Space", rb_cObject);
	rb_define_alloc_func(c_cpSpace, &rb_cpSpaceAlloc);
	rb_define_method(c_cpSpace, "initialize", &rb_cpSpaceInitialize, 1);
	
	rb_define_method(c_cpSpace, "iterations", &rb_cpSpaceGetIterations, 0);
	rb_define_method(c_cpSpace, "iterations=", &rb_cpSpaceSetIterations, 1);
	
	rb_define_method(c_cpSpace, "damping", &rb_cpSpaceGetDamping, 0);
	rb_define_method(c_cpSpace, "damping=", &rb_cpSpaceSetDamping, 1);
	
	rb_define_method(c_cpSpace, "gravity", &rb_cpSpaceGetGravity, 0);
	rb_define_method(c_cpSpace, "gravity=", &rb_cpSpaceSetGravity, 1);
	
	rb_define_method(c_cpSpace, "static_body", &rb_cpSpaceGetStaticBody, 0);

	// collision pair funcs
	
	rb_define_method(c_cpSpace, "add_shape", &rb_cpSpaceAddShape, 1);
	rb_define_method(c_cpSpace, "add_static_shape", &rb_cpSpaceAddStaticShape, 1);
	rb_define_method(c_cpSpace, "add_body", &rb_cpSpaceAddBody, 1);
	
	rb_define_method(c_cpSpace, "remove_shape", &rb_cpSpaceRemoveShape, 1);
	rb_define_method(c_cpSpace, "remove_static_shape", &rb_cpSpaceRemoveStaticShape, 1);
	rb_define_method(c_cpSpace, "remove_body", &rb_cpSpaceRemoveBody, 1);
	
	rb_define_method(c_cpSpace, "each_shape", &rb_cpSpaceEachShape, 0);
	rb_define_method(c_cpSpace, "each_static_shape", &rb_cpSpaceEachStaticShape, 0);
	rb_define_method(c_cpSpace, "each_body", &rb_cpSpaceEachBody, 0);
	
	rb_define_method(c_cpSpace, "resize_static_hash", &rb_cpSpaceResizeStaticHash, 2);
	rb_define_method(c_cpSpace, "resize_active_hash", &rb_cpSpaceResizeActiveHash, 2);
	rb_define_method(c_cpSpace, "rehash_static", &rb_cpSpaceRehashStatic, 0);
	
	rb_define_method(c_cpSpace, "step", &rb_cpSpaceStep, 1);
}
